package edu.bu.manateem.msg.model.isotope;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;

public class IsotopeUtils
{
	private static Map<String,TreeSet<Peak>> ELEMENTS = null;
	
	public static Map<String,TreeSet<Peak>> getElements()
		throws Exception
	{
		if (IsotopeUtils.ELEMENTS != null)
		{
			return IsotopeUtils.ELEMENTS;
		}
		
		if (!IsotopeUtils.initElements())
		{
			throw new Exception("Could not initialize elemets from "
				+ IsotopeConstants.ELEMENT_FILE_URL);
		}
		
		return IsotopeUtils.ELEMENTS;
	}
	
	private static boolean initElements()
	{
		IsotopeUtils.ELEMENTS = new HashMap<String,TreeSet<Peak>>();
		BufferedReader data;
		int count;
		Peak peak;
		try
		{
			URL url = new URL(IsotopeConstants.ELEMENT_FILE_URL);
			data = new BufferedReader(new InputStreamReader(url.openStream()));
			
			data.readLine(); // read table headings
			data.readLine();
			data.readLine(); // get first barred line
			String line;
			
			while ((line = data.readLine()) != null)
			{
				String[] split = IsotopeUtils.splitElementeLine(line);
				String symbol = split[1];
				
				TreeSet<Peak> peaks = new TreeSet<Peak>();
				if (!symbol.equals(""))
				{
					IsotopeUtils.ELEMENTS.put(symbol,peaks);
				}
				
				peak = IsotopeUtils.parseIsotope(split);
				
				// don't add isotopes with 0 probability since they don't affect
				// results
				// but increase computing time.
				if ((peak != null) && (peak.getP() > 0.0))
				{
					peaks.add(peak);
				}
				
				while (((line = data.readLine()) != null)
					&& !line.startsWith("_"))
				{ // if the first char isn't a bar then this is an isotope line
					peak =
						IsotopeUtils.parseIsotope(IsotopeUtils.splitElementeLine(line));
					if ((peak != null) && (peak.getP() > 0.0))
					{
						peaks.add(peak);
					}
				}
				// data.readLine(); // otherwise read the rest of the bar line
			}
			data.close();
		}
		catch (IOException ioe)
		{
			return false;
		}
		return true;
	}
	
	private static Peak parseIsotope(String[] line)
	{
		// System.out.println(Arrays.toString(Arrays.copyOfRange(line, 3, 5)));
		if (line[4].trim().equals(""))
		{
			return null;
		}
		return new Peak(new Double(line[3]),new Double(line[4]) / 100.0);
	}
	
	public static double roundToDigits(double d,int digits)
	{
		if (digits > 10)
		{
			return d;
		}
		double mult = Math.pow(10,digits);
		return Math.rint(d * mult) / mult;
	}
	
	private static String[] splitElementeLine(String line)
	{
		line = String.format("%1$-74s",line);
		return new String[]
			{
				line.substring(0,4).trim(),
				line.substring(4,8).trim(),
				line.substring(8,13).trim(),
				line.substring(13,32).split("\\(")[0].trim(),
				line.substring(32,46).split("\\(")[0].trim()
			};
	}
	
	public static Peak strongerIsotope(Peak peak1,Peak peak2)
	{
		Peak stronger = peak1;
		if (peak2.getP() > stronger.getP())
		{
			stronger = peak2;
		}
		
		return stronger;
	}
	
	public static double sumP(Collection<Peak> isotopes)
	{
		double sum = 0;
		for (Peak i : isotopes)
		{
			sum += i.getP();
		}
		
		return sum;
	}
	
	/**
	 * Converts mass and charge to m/z
	 * @param mass
	 * @param charge
	 * @param negativeIons - the negative ion case
	 * @return
	 */
	public static double toMZ(double mass,int charge,boolean negativeIons)
	{
		if (charge == 1)
		{
			return mass - IsotopeConstants.ELECTRON_MASS;
		}
		
		double mZ;
		
		if (negativeIons)
		{
			mZ = (mass - charge) / charge;
		}
		else
		{
			mZ =
				(mass + ((charge - 1) * IsotopeConstants.PROTON_MASS) - IsotopeConstants.ELECTRON_MASS)
					/ charge;
			
		}
		return mZ;
	}
	
}
